Làm bài tập →

Bài 4: Mảng và JSON trong JavaScript

1. Mảng (Array) là gì?

Mảng là một kiểu dữ liệu đặc biệt trong JavaScript, cho phép lưu trữ nhiều giá trị (phần tử) trong một biến duy nhất. Mỗi phần tử trong mảng được đánh số thứ tự (chỉ số) bắt đầu từ 0.

// Khai báo mảng
var fruits = ["Apple", "Banana", "Orange"];
  • Chỉ số (index) của phần tử đầu tiên là 0, phần tử thứ hai là 1, ...
  • Mảng có thể chứa nhiều kiểu dữ liệu khác nhau: số, chuỗi, đối tượng, v.v.

2. Làm việc với mảng: Truy cập, xem, sửa, thêm, xóa, tìm kiếm, duyệt, sắp xếp

Truy cập phần tử:
var fruits = ["Apple", "Banana", "Orange"];
console.log(fruits[0]); // Apple
console.log(fruits[2]); // Orange
Xem số lượng phần tử:
console.log(fruits.length); // 3
Sửa giá trị phần tử:
fruits[1] = "Mango";
console.log(fruits); // ["Apple", "Mango", "Orange"]
Thêm phần tử mới vào cuối mảng (push):
fruits.push("Grape");
console.log(fruits); // ["Apple", "Mango", "Orange", "Grape"]
Thêm phần tử vào đầu mảng (unshift):
fruits.unshift("Lemon");
console.log(fruits); // ["Lemon", "Apple", "Mango", "Orange", "Grape"]
Xóa phần tử cuối cùng (pop):

Phương thức pop() vừa xóa phần tử cuối cùng của mảng, vừa trả về giá trị phần tử đó trước khi xóa. Thường dùng khi cần lấy và loại bỏ phần tử cuối cùng (ví dụ: thao tác kiểu stack/LIFO).

var last = fruits.pop();
console.log(last); // "Grape" (giả sử mảng ban đầu có "Grape" ở cuối)
console.log(fruits); // ["Lemon", "Apple", "Mango", "Orange"]   
Xóa phần tử đầu tiên (shift):

Phương thức shift() vừa xóa phần tử đầu tiên của mảng, vừa trả về giá trị phần tử đó trước khi xóa. Thường dùng khi cần lấy và loại bỏ phần tử đầu tiên (ví dụ: thao tác kiểu queue/FIFO).

var first = fruits.shift();
console.log(first); // "Lemon" (giả sử mảng ban đầu có "Lemon" ở đầu)
console.log(fruits); // ["Apple", "Mango", "Orange"]
Xóa phần tử ở vị trí bất kỳ (splice):

Cú pháp: array.splice(vị_trí_bắt_đầu, số_lượng_phần_tử_cần_xóa)
- vị_trí_bắt_đầu: chỉ số phần tử đầu tiên muốn xóa (bắt đầu từ 0)
- số_lượng_phần_tử_cần_xóa: số phần tử sẽ bị xóa kể từ vị trí bắt đầu

fruits.splice(1, 1); // Xóa 1 phần tử tại vị trí 1 ("Mango")
console.log(fruits); // ["Apple", "Orange"];

// Xóa 2 phần tử từ vị trí 0
var arr = [1, 2, 3, 4];
arr.splice(0, 2); // Xóa 2 phần tử đầu tiên
console.log(arr); // [3, 4]
Nối mảng (concat):
var moreFruits = ["Pineapple", "Kiwi"];
var allFruits = fruits.concat(moreFruits);
console.log(allFruits); // ["Apple", "Orange", "Pineapple", "Kiwi"]
Tìm kiếm phần tử (indexOf, includes, find, findIndex, filter):
var numbers = [10, 20, 30, 40];
// indexOf: Tìm vị trí đầu tiên của giá trị 20
console.log(numbers.indexOf(20)); // 1
// includes: Kiểm tra mảng có chứa 30 không
console.log(numbers.includes(30)); // true

// find: Tìm phần tử đầu tiên là số chẵn
var even = numbers.find(x => x % 2 === 0);
console.log(even); // 10 (vì 10 là số chẵn đầu tiên)

// find: Tìm phần tử đầu tiên lớn hơn 15 và nhỏ hơn 35
var inRange = numbers.find(x => x > 15 && x < 35);
console.log(inRange); // 20

// findIndex: Tìm vị trí phần tử đầu tiên lớn hơn 25
var idx = numbers.findIndex(x => x > 25);
console.log(idx); // 2 (vì 30 là phần tử đầu tiên > 25)

// findIndex: Tìm vị trí phần tử đầu tiên là số lẻ (không có)
var oddIdx = numbers.findIndex(x => x % 2 !== 0);
console.log(oddIdx); // -1

// filter: Lọc ra tất cả các số lớn hơn 15
var filtered = numbers.filter(x => x > 15);
console.log(filtered); // [20, 30, 40]

// filter: Lọc ra các số chia hết cho 10
var divisibleBy10 = numbers.filter(x => x % 10 === 0);
console.log(divisibleBy10); // [10, 20, 30, 40]

// Tìm kiếm với mảng chuỗi
var names = ["Nam", "An", "Bình", "Ngọc"];
// Tìm tên bắt đầu bằng chữ N
var nameN = names.find(n => n.startsWith("N"));
console.log(nameN); // "Nam"
// Tìm vị trí tên có độ dài lớn hơn 3 ký tự
var longNameIdx = names.findIndex(n => n.length > 3);
console.log(longNameIdx); // 2 ("Bình")
// filter: Lọc ra các tên có 3 ký tự trở lên
var longNames = names.filter(n => n.length >= 3);
console.log(longNames); // ["Nam", "Bình", "Ngọc"]
Duyệt mảng (for, forEach, map, reduce):
var arr = [1, 2, 3];
for (var i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}
arr.forEach(function(item) {
    console.log(item);
});
var doubled = arr.map(x => x * 2); // [2, 4, 6]
console.log(doubled);

// Tính tổng các phần tử bằng reduce
var sum = arr.reduce(function(acc, curr) {
    return acc + curr;
}, 0);
console.log(sum); // 6

// Giải thích:
// reduce nhận vào một hàm (accumulator, currentValue) và giá trị khởi tạo (ở đây là 0).
// Hàm sẽ chạy qua từng phần tử, cộng dồn vào accumulator và trả về tổng cuối cùng.
Sắp xếp mảng (sort):

- sort() mặc định sắp xếp các phần tử theo thứ tự chuỗi Unicode (tức là chuyển về chuỗi rồi so sánh từng ký tự).
- Nếu mảng là số, cần truyền vào một hàm so sánh để sắp xếp đúng giá trị số.
- Nếu mảng là object, cần truyền vào hàm so sánh để chỉ định thuộc tính nào sẽ dùng để so sánh.

// Sắp xếp mảng số
var nums = [3, 1, 14, 2];
nums.sort(); // [1, 14, 2, 3] (sai với số lớn hơn 9 vì so sánh chuỗi)
nums.sort((a, b) => a - b); // [1, 2, 3, 14] (đúng thứ tự số)

Giải thích cú pháp (a, b) => a - b:
- sort() nhận vào một hàm so sánh có 2 tham số ab.
- Nếu hàm trả về giá trị âm (a - b < 0), a sẽ đứng trước b.
- Nếu hàm trả về 0, vị trí không đổi.
- Nếu hàm trả về dương (a - b > 0), b sẽ đứng trước a.
- a - b nghĩa là: nếu a < b thì trả về số âm, a đứng trước; nếu a > b thì trả về số dương, b đứng trước.
- Nhờ đó, các số sẽ được sắp xếp tăng dần đúng thứ tự số học.

// Ví dụ minh họa hàm so sánh:
function soSanh(a, b) {
    if (a < b) return -1; // a đứng trước b
    if (a > b) return 1;  // b đứng trước a
    return 0;             // không đổi vị trí
}
nums.sort(soSanh); // Kết quả giống sort((a, b) => a - b)

- Nếu không truyền hàm so sánh, sort sẽ chuyển các phần tử về chuỗi và so sánh từng ký tự (nên số 14 sẽ đứng trước số 2 vì "1" < "2").
- Để sắp xếp số đúng, luôn dùng sort((a, b) => a - b).
- Để sắp xếp object, truyền vào hàm so sánh dựa trên thuộc tính mong muốn.
- Để sắp xếp chuỗi tiếng Việt có dấu, nên dùng localeCompare để kết quả chính xác hơn.

So sánh chuỗi với localeCompare:
- localeCompare là phương thức của chuỗi, dùng để so sánh hai chuỗi theo đúng thứ tự bảng chữ cái của ngôn ngữ (có hỗ trợ tiếng Việt, có dấu).
- Khi sort chuỗi tiếng Việt hoặc thuộc tính chuỗi trong object, nên dùng localeCompare để kết quả chính xác.

// Sắp xếp mảng chuỗi tiếng Việt
var arr = ["Nam", "Ánh", "Bình", "Đức", "An"];
arr.sort(); // ["An", "Bình", "Nam", "Ánh", "Đức"] (sai thứ tự với tiếng Việt)
arr.sort((a, b) => a.localeCompare(b, 'vi'));
console.log(arr); // ["An", "Ánh", "Bình", "Đức", "Nam"]
// Sắp xếp mảng object theo tên có dấu
var students = [
  { name: "Ánh", age: 20 },
  { name: "Bình", age: 18 },
  { name: "Nam", age: 22 },
  { name: "An", age: 19 }
];
students.sort((a, b) => a.name.localeCompare(b.name, 'vi'));
console.log(students);
// [{name: "An", ...}, {name: "Ánh", ...}, {name: "Bình", ...}, {name: "Nam", ...}]

- localeCompare trả về số âm, 0, hoặc dương tương tự như hàm so sánh sort.
- Khi truyền thêm tham số 'vi', sort sẽ theo chuẩn tiếng Việt.

3. Array Const (Khai báo mảng với const)

Mảng khai báo với const không thể gán lại, nhưng có thể thay đổi phần tử bên trong.

const colors = ["red", "green", "blue"];
colors[0] = "yellow"; // Được phép
// colors = ["black"]; // Lỗi
console.log(colors);

4. JavaScript JSON

JSON (JavaScript Object Notation) là một định dạng chuỗi dùng để lưu trữ và trao đổi dữ liệu. JSON có thể mô tả dữ liệu dạng object hoặc array, rất phổ biến khi truyền dữ liệu giữa client và server, hoặc lưu trữ dữ liệu trên web.

So sánh Object và Array trong JSON:
  • Object (đối tượng): Dữ liệu dạng { key: value }, dùng để mô tả một thực thể với các thuộc tính. Ví dụ: {"name": "An", "age": 20}
  • Array (mảng): Dữ liệu dạng [value1, value2, ...], dùng để lưu danh sách các giá trị hoặc danh sách object. Ví dụ: [1, 2, 3] hoặc [{"name": "An"}, {"name": "Bình"}]
Tổng kết: JSON, Object và Array
  • ObjectArray là hai kiểu dữ liệu gốc trong JavaScript, dùng để lưu trữ và thao tác dữ liệu trong chương trình.
  • JSON chỉ là một chuỗi (string) biểu diễn dữ liệu theo định dạng đặc biệt, có thể mô tả object hoặc array.
  • Khi truyền dữ liệu qua mạng, lưu trữ, hoặc giao tiếp giữa các hệ thống, object/array cần được chuyển thành chuỗi JSON (dùng JSON.stringify).
  • Khi nhận dữ liệu từ bên ngoài (server, file, localStorage), chuỗi JSON cần được chuyển lại thành object/array (dùng JSON.parse).
  • JSON không hỗ trợ function, undefined, symbol, chỉ lưu trữ dữ liệu kiểu số, chuỗi, boolean, null, object, array.

5. Tổng kết

- Mảng là kiểu dữ liệu quan trọng trong JavaScript, cho phép lưu trữ và thao tác với tập hợp dữ liệu.
- JSON là định dạng dữ liệu phổ biến, dùng để trao đổi dữ liệu giữa client và server.